﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Ionic.Zip;
using LLoger;
using OpenPop.Mime;
using OpenPop.Pop3;
using TSRepLoader.Pop;
using TSRepLoader.Properties;

namespace TSRepLoader
{
    class Program
    {
        public class ConsoleLog : ILogWriter
        {
            private readonly ILogWriter _lw;

            public ConsoleLog(ILogWriter lw)
            {
                _lw = lw;
            }

            public void Dispose()
            {
                _lw.Dispose();
            }

            public void WriteLine(string val)
            {
                _lw.WriteLine(val);
                Console.WriteLine(val);
            }

            public void WriteLine(string val, params object[] args)
            {
                _lw.WriteLine(val, args);

            }

            public void WriteLine(Exception ex)
            {
                _lw.WriteLine(ex);
            }

            public void WriteLine(Exception ex, string extMsg)
            {
                _lw.WriteLine(ex, extMsg);
            }
        }

        private static class StartParams
        {
            public static bool SkipForts = false;
            public static bool SkipMicex = false;
            public static DateTime LoadDate = DateTime.Today;
            public static int LoadDepth = 20;
            public static string CleanupAction = "";
        }


        class Recusion
        {
            private int _recN = 0;
            public void MainProc()
            {
                string str = "Start.s.a.b.c";

                if (!RecusionProc(ref str))
                    Console.WriteLine("False result from recursion Proc. And str = " + str);
                else
                    Console.WriteLine("TRUE result from recursion Proc. And str = " + str);
            }

            private bool RecusionProc(ref string str)
            {
                _recN++;
                Console.WriteLine("RecusionProc iteration: " + _recN);

                if (str == "Start")
                {
                    Console.WriteLine("String is 'Start'. Recusion iterations: " + _recN);
                    return true;
                }

                bool res = HandleA(ref str) || HandleB(ref str) || HandleC(ref str);
                if (res)
                {
                    Console.WriteLine(_recN + " Handling result: " + res);

                    res = RecusionProc(ref str);
                    Console.WriteLine(_recN + " Recursion result: " + res);
                }

                _recN--;
                return res;
            }

            private bool HandleA(ref string str)
            {
                if (str.EndsWith(".a"))
                {
                    str = str.Replace(".a", "");
                    return true;
                }

                return false;
            }

            private bool HandleB(ref string str)
            {
                if (str.EndsWith(".b"))
                {
                    str = str.Replace(".b", "");
                    return true;
                }

                return false;
            }

            private bool HandleC(ref string str)
            {
                if (str.EndsWith(".c"))
                {
                    str = str.Replace(".c", "");
                    return true;
                }

                return false;
            }
        }

        static void Main(string[] args)
        {
            /* new Recusion().MainProc();
             Console.ReadKey();
             return;
             */

            ILogWriter lw = new ConsoleLog(new FileLogWriter("TSRepLoader.log", 0));
            lw.WriteLine("Старт сервиса");
            if (args != null && args.Length > 0)
                ParseStartArgs(args, lw);

            if (!StartParams.SkipForts)
                HandleFortsReports(lw);

            if (!StartParams.SkipMicex)
                HandleMicexReports(lw);

            try
            {
                CleanUpDownLoadDir();
            }
            catch (Exception ex)
            {
                lw.WriteLine(ex, "ERROR:");
            }

            lw.Dispose();
            Console.WriteLine("Конец");
        }

        private static void CleanUpDownLoadDir()
        {
            if (!"delzip".Contains(StartParams.CleanupAction))
                return;

            if (StartParams.CleanupAction == "zip")
                try
                {
                    var arcDir = Path.Combine(Settings.Default.DownloadPath, "Archive");
                    if (!Directory.Exists(arcDir))
                        Directory.CreateDirectory(arcDir);

                    var zip = new ZipFile();
                    zip.AddFiles(
                        Directory.EnumerateFiles(Properties.Settings.Default.DownloadPath),false,"");
                    zip.Save(Path.Combine(arcDir, "rep_" + DateTime.Today.ToString("yyyyMMdd") + ".zip"));

                    SilentCleanDir();
                }
                catch (Exception ex)
                {
                    throw new Exception("Unable to zip source reports", ex);
                }

            if (StartParams.CleanupAction == "del")
                try
                {
                    SilentCleanDir();
                }
                catch (Exception ex)
                {
                    throw new Exception("Unable to delete source reports", ex);
                }

        }

        private static void SilentCleanDir()
        {
            foreach (var file in Directory.EnumerateFiles(Settings.Default.DownloadPath))
                try
                {
                    File.Delete(file);
                }
                catch
                {
                }
        }

        private static void HandleMicexReports(ILogWriter lw)
        {
            try
            {
                var account = PopAccount.Get(Properties.Settings.Default.MicexEmail);
                using (MailDownloader md = new MicexDownloader(account.Host, account.Port, account.Ssl, account.User,
                                                               account.Password, lw))
                {
                    md.SetLoadDepth(StartParams.LoadDepth);
                    md.Load(StartParams.LoadDate);
                }
            }
            catch (Exception ex)
            {
                lw.WriteLine(ex, "ERROR: Unable to load and handle MICEX report");
            }
        }

        private static void HandleFortsReports(ILogWriter lw)
        {
            try
            {
                var account = PopAccount.Get(Properties.Settings.Default.FortsEmail);
                using (MailDownloader md = new FortsDownloader(account.Host, account.Port, account.User,
                                                               account.Password, lw))
                {
                    md.SetLoadDepth(StartParams.LoadDepth);
                    md.Load(StartParams.LoadDate);
                }
            }
            catch (Exception ex)
            {
                lw.WriteLine(ex, "ERROR: Unable to load and handle FORTS report");
            }
        }

        /// <summary>
        /// TSRepLoader [--forts|--micex] [-d:dd.mm.yyyy]
        /// Парсит входные аргументы.
        /// --forts отключить фортс
        /// --micex отключить ммвб
        /// -d:dd.mm.yyyy задать дату поска писем/отчетов. Если параметр не указан, берется текущая
        /// -ld:20 глубина загрузки писем по умолчанию = 20
        /// -cleanup:<del|zip> действие с загруженными аттачами в папке загрузки (конфиг - DownloadPath): del - удалить, zip - сохранить в архив с именем rep_yyyyMMdd.zip
        /// </summary>
        /// <param name="args"></param>
        /// <param name="lw"></param>
        private static void ParseStartArgs(string[] args, ILogWriter lw)
        {
            foreach (var s in args)
            {
                var arg = s.ToLower();

                if (arg == "--forts")
                    StartParams.SkipForts = true;

                if (arg == "--micex")
                    StartParams.SkipMicex = true;

                if (arg.StartsWith("-ld:"))
                    try
                    {
                        StartParams.LoadDepth = int.Parse(arg.Split(':')[1]);
                    }
                    catch (Exception ex)
                    {
                        lw.WriteLine(ex, "WARNING: Incorrect -ld (Load depth) parameter " + s);
                    }

                if (arg.StartsWith("-help"))
                {
                    Console.WriteLine(
                        // ReSharper disable LocalizableElement
                        "TSRepLoader [--forts|--micex] [-d:dd.mm.yyyy] [-ld:X] [-help]\r\n" +
                        "--forts отключить фортс\r\n" +
                        "--micex отключить ммвб\r\n" +
                        "-d:dd.mm.yyyy задать дату поска писем/отчетов. Если параметр не указан, берется текущая\r\n" +
                        "-ld:20 глубина загрузки писем по умолчанию = 20\r\n" +
                        "-cleanup:<del|zip> действие с загруженными аттачами в папке загрузки (конфиг - DownloadPath):\r\n" +
                        "       del - удалить, zip - сохранить в архив с именем rep_yyyyMMdd.zip");
                    // ReSharper restore LocalizableElement
                    Console.ReadKey();
                    Environment.Exit(0);
                }

                if (arg.Contains("-cleanup:"))
                    try
                    {
                        var action = arg.Split(':')[1];

                        if ("delzip".Contains(action))
                            StartParams.CleanupAction = action;
                        else
                            throw new ArgumentException("invalid parameter " + arg);

                    }
                    catch (Exception ex)
                    {
                        lw.WriteLine(ex, "ERROR: wrong cleanup action parameter: " + s+". Will use zip as default");
                        StartParams.CleanupAction = "zip";
                    }

                if (arg.Contains("-d:"))
                    try
                    {
                        var dParam = arg.Split(':');
                        if (dParam.Length != 2)
                            return;

                        DateTime date = new DateTime(
                            int.Parse(dParam[1].Substring(6, 4)),
                            int.Parse(dParam[1].Substring(3, 2)),
                            int.Parse(dParam[1].Substring(0, 2))
                            );

                        StartParams.LoadDate = date;
                    }
                    catch (Exception ex)
                    {
                        lw.WriteLine(ex, "ERROR: wrong date parameter: " + s);
                    }

            }
        }
    }

    internal class PopAccount
    {
        public string Host;
        public int Port;
        public bool Ssl;
        public string User;
        public string Password;

        protected PopAccount()
        {
        }

        /// <summary>
        /// parse from aacount string having format host;port;ssl;user;password
        /// </summary>
        /// <param name="accountString"></param>
        /// <returns></returns>
        public static PopAccount Get(string accountString)
        {
            var splited = accountString.Split(';');

            if (splited.Length != 5)
                throw new ArgumentException("Wrong account parameter: " + accountString, accountString);

            return new PopAccount
            {
                Host = splited[0],
                Port = int.Parse(splited[1]),
                Ssl = bool.Parse(splited[2]),
                User = splited[3],
                Password = splited[4]
            };

        }
    }
}
